/****************************************************************
 * Fulguro
 * Copyright (C) 2004 Christophe Clienti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 ***************************************************************/

#include <string.h>
#include "flgrCoreMalloc.h"
#include "flgrCoreNhbManage.h"
#include "flgrCoreDispatch.h"
#include "flgrCoreDataIO.h"

/*!
 * \defgroup group_fulguro_core_nhb Neighborhood extraction
 * \ingroup group_fulguro_core
 * \brief Functions to manipulate and extract neighborhood in images
 * The extraction of a neighborhood is done for each FLGR_Data2D source pixels. A computation is done and
 * the corresponding pixels in the destination is replace2dd by the result.
 * \see group_fulguro_core_slide
 * @{
 */

/*************************************** 1D Functions ******************************************/

//////////////////////////////////////////////////////////////////
/*! Return optimization flag for a neighborhood operation on a 1D signal
 *  @param datdest : a pointer to a FLGR_Data1D
 *  @param datsrc  : a pointer to a FLGR_Data1D
 *  @param nhb  : a pointer to a FLGR_Data1D
 *  @returns optimization flag
 */
//////////////////////////////////////////////////////////////////
FLGR_MorphoOptim flgr1d_get_optimization_available(FLGR_Data1D *datdest, FLGR_Data1D *datsrc, FLGR_Data1D *nhb) {
  FLGR_DECLARE_FUNCTION;
  POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_NONE\n");
  return FLGR_MORPHO_OPTIM_NONE;
}


//////////////////////////////////////////////////////////////////
/*! Allocate a 1D neighborhood box
 *  @warning A special issue is made for fgBIT type. A fgBIT type could embedded 32 or 64 pixels but when
 *  pixels are extracted from the image only one pixel is store in a fgBIT element of the extr array
 *  to simplify processing
 *  @param data : a pointer to a FLGR_Data1D
 *  @returns a pointer to a FLGR_NhbBox1D
 */
//////////////////////////////////////////////////////////////////
FLGR_NhbBox1D *flgr1d_create_neighbor_box(FLGR_Data1D *data) {
  FLGR_NhbBox1D *tmp;
  int typeSize, sizeMax,k;
  
  FLGR_DECLARE_FUNCTION;

  if(data==NULL) {
    POST_ERROR("Null objects!\n");
    return NULL;
  }

  typeSize = flgr_get_sizeof(data->type);
  sizeMax = data->length;

  if(typeSize < 1) return NULL;
  if(data->spp < 1) return NULL;

  tmp                   = flgr_malloc(sizeof(FLGR_NhbBox1D));
  
  tmp->type             = data->type;
  tmp->spp              = data->spp;

  tmp->center_data_val  = flgr_vector_create(data->spp, data->type);
  tmp->center_nhb_val   = flgr_vector_create(data->spp, data->type);
  tmp->center_coord_x   = 0;
  tmp->nhb_size_x       = data->length;

  tmp->list_coord_x     = (int **) flgr_malloc(sizeof(int*) * data->spp);
  
  tmp->list_data_val    = flgr_malloc(sizeof(void*) * data->spp);
  tmp->list_nhb_val     = flgr_malloc(sizeof(void*) * data->spp);

  tmp->size             = (int *) flgr_malloc(sizeof(int) * data->spp);

  for(k=0 ; k<data->spp ; k++) {
    tmp->list_coord_x[k] = flgr_malloc(sizeof(int) * sizeMax);
    tmp->list_data_val[k] = flgr_malloc(flgr_get_sizeof(tmp->type) * sizeMax);
    tmp->list_nhb_val[k] = flgr_malloc(flgr_get_sizeof(tmp->type) * sizeMax);
    tmp->size[k]=0;
  }

  return tmp;
  
}



//////////////////////////////////////////////////////////////////
/*! Destroy a neighborhood box
 *  @param extr : a pointer to a FLGR_NhbBox1D
 *  @returns nothing
 */
//////////////////////////////////////////////////////////////////
void flgr1d_destroy_neighbor_box(FLGR_NhbBox1D *extr) {
  int k;
  FLGR_DECLARE_FUNCTION;

  if(extr==NULL) {
    POST_ERROR("Null objects!\n");
    return;
  }

  flgr_vector_destroy(extr->center_data_val);
  flgr_vector_destroy(extr->center_nhb_val);

  flgr_free(extr->size);

  for(k=0 ; k<extr->spp ; k++) {
    flgr_free(extr->list_coord_x[k]);
    flgr_free(extr->list_data_val[k]);
    flgr_free(extr->list_nhb_val[k]);
  }

  flgr_free(extr->list_coord_x);
  flgr_free(extr->list_data_val);
  flgr_free(extr->list_nhb_val);

  flgr_free(extr);
}



//////////////////////////////////////////////////////////////////
// Macro for different type of getNeighborhood_dtype
//////////////////////////////////////////////////////////////////
#define FLGR_MACRO_GET_NHB_1D_IN_DATA(dtype)				\
  int j,k;								\
  int l,m;								\
  int nbpos;								\
  int startpos,stoppos;							\
  int nhbsize_xdiv2;							\
  int spp = dat->spp;							\
  dtype *pnhb = (dtype *) nhb->array;					\
  dtype *pdat = (dtype *) dat->array;					\
  dtype *list_data;							\
  dtype *list_nhb;							\
  int *list_coord_x;							\
  int *size = extr->size;						\
  dtype nhb_val,data_val;						\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  nhbsize_xdiv2=(nhb->length)>>1;					\
									\
  extr->center_coord_x=pos;						\
									\
  nbpos=dat->length;							\
									\
  startpos=pos - nhbsize_xdiv2;						\
  if(startpos<0) startpos=0;						\
									\
  stoppos=pos + nhbsize_xdiv2;						\
  if(stoppos>=nbpos) stoppos=nbpos-1;					\
									\
									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_data_val->array), \
				     (dtype*) (dat->array),		\
				     spp, pos);				\
									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_nhb_val->array), \
				     (dtype*) (nhb->array),		\
				     spp, nhbsize_xdiv2);		\
									\
									\
  for(k=0 ; k<spp ; k++) {						\
    l=startpos-pos+ nhbsize_xdiv2;					\
    list_data = (dtype*) extr->list_data_val[k];			\
    list_nhb = (dtype*) extr->list_nhb_val[k];				\
    list_coord_x = (int*) extr->list_coord_x[k];			\
    m=0;								\
    for(j=startpos ; j<=stoppos ; j++,l++) {				\
      nhb_val = flgr1d_get_data_array_##dtype(pnhb, l*spp+k);		\
      if(nhb_val!=0) {							\
	data_val = flgr1d_get_data_array_##dtype(pdat, j*spp+k);	\
	list_coord_x[m] = l;						\
	list_data[m] = data_val;					\
 	list_nhb[m] = nhb_val;						\
	m++;								\
      }									\
    }									\
    size[k]=m;								\
  }									\
									\
  return




void flgr1d_get_neighborhood_fgBIT(FLGR_NhbBox1D *extr,
				   FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgBIT);
}

void flgr1d_get_neighborhood_fgUINT8(FLGR_NhbBox1D *extr,
				     FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgUINT8);
}

void flgr1d_get_neighborhood_fgUINT16(FLGR_NhbBox1D *extr,
				      FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgUINT16);
}

void flgr1d_get_neighborhood_fgUINT32(FLGR_NhbBox1D *extr,
				      FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgUINT32);
}

void flgr1d_get_neighborhood_fgINT8(FLGR_NhbBox1D *extr,
				    FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgINT8);
}

void flgr1d_get_neighborhood_fgINT16(FLGR_NhbBox1D *extr,
				     FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgINT16);
}

void flgr1d_get_neighborhood_fgINT32(FLGR_NhbBox1D *extr,
				     FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgINT32);
}

void flgr1d_get_neighborhood_fgFLOAT32(FLGR_NhbBox1D *extr,
				       FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgFLOAT32);
}

void flgr1d_get_neighborhood_fgFLOAT64(FLGR_NhbBox1D *extr,
				       FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA(fgFLOAT64);
}


//////////////////////////////////////////////////////////////////
/*! Extract a neighborhood in an image and copy to a neighborhood box
 *  Only relevant pixels are copied to neighborhood box.
 *  So the user must take car to extr->size to do computation
 *  @param extr : a pointer to a FLGR_NhbBox1D
 *  @param dat  : a pointer to a FLGR_Data1D considered as an image
 *  @param nhb  : a pointer to a FLGR_Data1D considered as a neighborhood definition
 *  @param pos  : index
 *  @returns FLGR_NhbBox1D
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr1d_get_neighborhood(FLGR_NhbBox1D *extr, 
				 FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  
  FLGR_DECLARE_FUNCTION;

  if(dat==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(nhb==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(extr==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(dat->type != nhb->type) {
    POST_ERROR("Image structure and Neighborhood structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->type != nhb->type) {
    POST_ERROR("Neighbor Box and Neighborhood structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->nhb_size_x!=nhb->length) {
    POST_ERROR("Neighbor Box and Neighborhood structure have differents size_x (%d and %d)!\n",
	       extr->nhb_size_x,nhb->length);
    return FLGR_RET_SIZE_ERROR;
  }

   FLGR_DISPATCH_PROCEDURE(nhb->type,flgr1d_get_neighborhood,extr,dat,nhb,pos);
  
  return FLGR_RET_OK;
}
  





//////////////////////////////////////////////////////////////////
// Macro for different type of getNeighborhood_dtype
//////////////////////////////////////////////////////////////////
#define FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(dtype)			\
  int j,k,jj;								\
  int l,m;								\
  int nbpos;								\
  int startpos,stoppos;							\
  int nhbsize_xdiv2;							\
  int spp = dat->spp;							\
  dtype *pnhb = (dtype *) nhb->array;					\
  dtype *pdat = (dtype *) dat->array;					\
  dtype *list_data;							\
  dtype *list_nhb;							\
  int *list_coord_x;							\
  int *size = extr->size;						\
  dtype nhb_val,data_val;						\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  nhbsize_xdiv2=(nhb->length)>>1;					\
									\
  extr->center_coord_x=pos;						\
									\
  nbpos=dat->length;							\
									\
  startpos=pos - nhbsize_xdiv2;						\
  									\
  stoppos=pos + nhbsize_xdiv2;						\
  									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_data_val->array), \
				     (dtype*) (dat->array),		\
				     spp, pos);				\
									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_nhb_val->array), \
				     (dtype*) (nhb->array),		\
				     spp, nhbsize_xdiv2);		\
									\
									\
  for(k=0 ; k<spp ; k++) {						\
    list_data = (dtype*) extr->list_data_val[k];			\
    list_nhb = (dtype*) extr->list_nhb_val[k];				\
    list_coord_x = (int*) extr->list_coord_x[k];			\
    m=0;								\
    for(j=startpos,l=0 ; j<=stoppos ; j++,l++) {			\
      nhb_val = flgr1d_get_data_array_##dtype(pnhb, l*spp+k);		\
      jj=flgr_normalize_coordinate(j,dat->length);			\
      data_val = flgr1d_get_data_array_##dtype(pdat, jj*spp+k);		\
      list_coord_x[m] = l;						\
      list_data[m] = data_val;						\
      list_nhb[m] = nhb_val;						\
      m++;								\
    }									\
    size[k]=m;								\
  }									\
									\
  return




void flgr1d_get_neighborhood_unfolded_fgBIT(FLGR_NhbBox1D *extr,
					    FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgBIT);
}

void flgr1d_get_neighborhood_unfolded_fgUINT8(FLGR_NhbBox1D *extr,
					      FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgUINT8);
}

void flgr1d_get_neighborhood_unfolded_fgUINT16(FLGR_NhbBox1D *extr,
					       FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgUINT16);
}

void flgr1d_get_neighborhood_unfolded_fgUINT32(FLGR_NhbBox1D *extr,
					       FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgUINT32);
}

void flgr1d_get_neighborhood_unfolded_fgINT8(FLGR_NhbBox1D *extr,
					     FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgINT8);
}

void flgr1d_get_neighborhood_unfolded_fgINT16(FLGR_NhbBox1D *extr,
					      FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgINT16);
}

void flgr1d_get_neighborhood_unfolded_fgINT32(FLGR_NhbBox1D *extr,
					      FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgINT32);
}

void flgr1d_get_neighborhood_unfolded_fgFLOAT32(FLGR_NhbBox1D *extr,
						FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgFLOAT32);
}

void flgr1d_get_neighborhood_unfolded_fgFLOAT64(FLGR_NhbBox1D *extr,
						FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  FLGR_MACRO_GET_NHB_1D_IN_DATA_UNFOLD(fgFLOAT64);
}


//////////////////////////////////////////////////////////////////
/*! Extract a neighborhood in an image and copy to a neighborhood box
 *  Only relevant pixels are copied to neighborhood box.
 *  So the user must take car to extr->size to do computation
 *  @param extr : a pointer to a FLGR_NhbBox1D
 *  @param dat  : a pointer to a FLGR_Data1D considered as an image
 *  @param nhb  : a pointer to a FLGR_Data1D considered as a neighborhood definition
 *  @param pos  : index
 *  @returns FLGR_NhbBox1D
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr1d_get_neighborhood_unfolded(FLGR_NhbBox1D *extr, 
					  FLGR_Data1D *dat, FLGR_Data1D *nhb, int pos) {
  
  FLGR_DECLARE_FUNCTION;

  if(dat==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(nhb==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(extr==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(dat->type != nhb->type) {
    POST_ERROR("Image structure and Neighborhood structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->type != nhb->type) {
    POST_ERROR("Neighbor Box and Neighborhood structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->nhb_size_x!=nhb->length) {
    POST_ERROR("Neighbor Box and Neighborhood structure have differents size_x (%d and %d)!\n",
	       extr->nhb_size_x,nhb->length);
    return FLGR_RET_SIZE_ERROR;
  }

   FLGR_DISPATCH_PROCEDURE(nhb->type,flgr1d_get_neighborhood_unfolded,extr,dat,nhb,pos);
  
  return FLGR_RET_OK;
}
  







/*************************************** 2D Functions ******************************************/


//////////////////////////////////////////////////////////////////
/*! Return optimization flag for a neighborhood operation on a 2D image
 *  @param datdest : a pointer to a FLGR_Data2D
 *  @param datsrc  : a pointer to a FLGR_Data2D
 *  @param nhb  : a pointer to a FLGR_Data2D
 *  @returns optimization flag
 */
//////////////////////////////////////////////////////////////////
FLGR_MorphoOptim flgr2d_get_optimization_available(FLGR_Data2D *datdest, FLGR_Data2D *datsrc, FLGR_Data2D *nhb) {
  FLGR_DECLARE_FUNCTION;

  if(datdest==datsrc) {
    POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_NONE\n");
    return FLGR_MORPHO_OPTIM_NONE;
  }

  

  if( (flgr2d_data_is_connexity(nhb,FLGR_8_CONNEX)) && (flgr2d_data_is_shape(nhb,FLGR_RECT)) && (datsrc->type != FLGR_BIT)) { 
    if((nhb->size_x==nhb->size_y) && (nhb->size_x<13)) {
      POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_SQUARE\n");
      return FLGR_MORPHO_OPTIM_SQUARE;
    }else if(datsrc->spp==1) {
      POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_RECTANGLE\n");
      return FLGR_MORPHO_OPTIM_RECTANGLE;
    }
  }else if((flgr2d_data_is_connexity(nhb,FLGR_8_CONNEX)) && (flgr2d_data_is_shape(nhb,FLGR_RECT)) && (datsrc->type == FLGR_BIT)){
    POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_SQUARE\n");
    return FLGR_MORPHO_OPTIM_SQUARE;
  }

  if((flgr2d_data_is_connexity(nhb,FLGR_8_CONNEX)) && (flgr2d_data_is_shape(nhb,FLGR_OCTAGON)) ) {
    if(nhb->size_x==nhb->size_y) {
      POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_OCTAGON\n");
      return FLGR_MORPHO_OPTIM_OCTAGON;
    }
  }

  if((flgr2d_data_is_connexity(nhb,FLGR_6_CONNEX)) && (flgr2d_data_is_shape(nhb,FLGR_HEX)) ) {
    if(nhb->size_x==nhb->size_y) {
      POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_HEXAGON\n");
      return FLGR_MORPHO_OPTIM_HEXAGON;
    }
  }

  if((flgr2d_data_is_connexity(nhb,FLGR_4_CONNEX)) && (flgr2d_data_is_shape(nhb,FLGR_CROSS)) ) {
    if((nhb->size_x==nhb->size_y) && (nhb->size_x==3)) {
      POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_CROSS\n");
      return FLGR_MORPHO_OPTIM_CROSS;
    }
  }

  if((flgr2d_data_is_connexity(nhb,FLGR_8_CONNEX)) && (flgr2d_data_is_shape(nhb,FLGR_DIAMOND)) ) {
    if(nhb->size_x==nhb->size_y) {
      POST_DEBUG("Optimization flag detected : FLGR_MORPHO_OPTIM_DIAMOND\n");
      return FLGR_MORPHO_OPTIM_DIAMOND;
    }
  }

  return FLGR_MORPHO_OPTIM_NONE;

}





//////////////////////////////////////////////////////////////////
/*! Allocate a 2D neighborhood box
 *  @warning A special issue is made for fgBIT type. A fgBIT type could embedded 32 or 64 pixels but when
 *  pixels are extracted from the image only one pixel is store in a fgBIT element of the extr array
 *  to simplify processing
 *  @param data : a pointer to a FLGR_Data2D
 *  @returns a pointer to a FLGR_NhbBox2D
 */
//////////////////////////////////////////////////////////////////
FLGR_NhbBox2D *flgr2d_create_neighbor_box(FLGR_Data2D *data) {
  FLGR_NhbBox2D *tmp;
  int sizeMax;
  int typeSize,k;

  FLGR_DECLARE_FUNCTION;

  if(data==NULL) {
    POST_ERROR("Null objects!\n");
    return NULL;
  }
  
  typeSize = flgr_get_sizeof(data->type);
  sizeMax = data->size_x*data->size_y;

  if(typeSize < 1) return NULL;
  if(data->spp < 1) return NULL;



  tmp                   = flgr_malloc(sizeof(FLGR_NhbBox2D));

  tmp->type             = data->type;
  tmp->spp              = data->spp;

  tmp->center_data_val  = flgr_vector_create(data->spp, data->type);
  tmp->center_nhb_val   = flgr_vector_create(data->spp, data->type);
  tmp->center_coord_y   = 0;
  tmp->center_coord_x   = 0;
  tmp->nhb_size_y       = data->size_y;
  tmp->nhb_size_x       = data->size_x;

  tmp->list_coord_x     = (int **) flgr_malloc(sizeof(int*) * data->spp);
  tmp->list_coord_y     = (int **) flgr_malloc(sizeof(int*) * data->spp);
  tmp->list_data_val    = (void **) flgr_malloc(sizeof(void*) *data->spp);
  tmp->list_nhb_val     = (void **) flgr_malloc(sizeof(void*) *data->spp);

  tmp->size             = (int *) flgr_malloc(sizeof(int) * data->spp);

  for(k=0 ; k<data->spp ; k++) {
    tmp->list_coord_y[k] = flgr_malloc(sizeof(int) * sizeMax);
    tmp->list_coord_x[k] = flgr_malloc(sizeof(int) * sizeMax);
    tmp->list_data_val[k] = flgr_malloc(flgr_get_sizeof(tmp->type) * sizeMax);
    tmp->list_nhb_val[k] = flgr_malloc(flgr_get_sizeof(tmp->type) * sizeMax);
    tmp->size[k]=0;
  }


  return tmp;

}


//////////////////////////////////////////////////////////////////
/*! Destroy a neighborhood box
 *  @param extr : a pointer to a FLGR_NhbBox2D
 *  @returns nothing
 */
//////////////////////////////////////////////////////////////////
void flgr2d_destroy_neighbor_box(FLGR_NhbBox2D *extr) {
  int k;
  FLGR_DECLARE_FUNCTION;

  if(extr==NULL) {
    POST_ERROR("Null objects!\n");
    return;
  }

  for(k=0 ; k<extr->spp ; k++) {
    flgr_free(extr->list_data_val[k]);
    flgr_free(extr->list_nhb_val[k]);
    flgr_free(extr->list_coord_x[k]);
    flgr_free(extr->list_coord_y[k]);
  }

 
  flgr_vector_destroy(extr->center_data_val);
  flgr_vector_destroy(extr->center_nhb_val);

  flgr_free(extr->list_coord_x);
  flgr_free(extr->list_coord_y);
  flgr_free(extr->list_data_val);
  flgr_free(extr->list_nhb_val);
  flgr_free(extr->size);

  flgr_free(extr);
}


//////////////////////////////////////////////////////////////////
// Macro for different type of getNeighborhood_dtype
//////////////////////////////////////////////////////////////////
#define FLGR_MACRO_GET_NHB_2D_IN_DATA(dtype)				\
  int i,j,m;								\
  int k,l,n;								\
  int nbrow,nbcol;							\
  int startrow,stoprow;							\
  int startcol,stopcol;							\
  int nhbsize_xdiv2;							\
  int nhbsize_ydiv2;							\
  int spp = dat->spp;							\
  dtype **pnhb = (dtype **) nhb->array;					\
  dtype **pdat = (dtype **) dat->array;					\
  dtype *list_data;							\
  dtype *list_nhb;							\
  int *list_coord_x;							\
  int *list_coord_y;							\
  int *size = extr->size;						\
  dtype nhb_val,data_val;						\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  nhbsize_xdiv2=(nhb->size_x)>>1;					\
  nhbsize_ydiv2=(nhb->size_y)>>1;					\
  nbrow=dat->size_y; nbcol=dat->size_x;					\
									\
  extr->center_coord_y=row;						\
  extr->center_coord_x=col;						\
									\
  startrow=row - nhbsize_ydiv2;						\
  startrow = (startrow<0) ? 0 : startrow;				\
									\
  stoprow=row + nhbsize_ydiv2;						\
  stoprow = (stoprow>=nbrow) ? nbrow-1 : stoprow;			\
									\
  startcol=col - nhbsize_xdiv2;						\
  startcol = (startcol<0) ? 0 : startcol;				\
									\
  stopcol=col + nhbsize_xdiv2;						\
  stopcol = (stopcol>=nbcol) ? nbcol-1 : stopcol;			\
									\
									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_data_val->array), \
				     (dtype*) (dat->array[row]),	\
				     spp, col);				\
									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_nhb_val->array), \
				     (dtype*) (nhb->array[nhbsize_ydiv2]), \
				     spp, nhbsize_xdiv2);		\
									\
									\
									\
  for(n=0 ; n<spp ; n++) {						\
    list_data = (dtype*) extr->list_data_val[n];			\
    list_nhb = (dtype*) extr->list_nhb_val[n];				\
    list_coord_x = (int*) extr->list_coord_x[n];			\
    list_coord_y = (int*) extr->list_coord_y[n];			\
    m=0;								\
    k=startrow-row+nhbsize_ydiv2;					\
    for(i=startrow ; i<=stoprow ; i++,k++) {				\
									\
      l=startcol-col+nhbsize_xdiv2;					\
    									\
      for(j=startcol ; j<=stopcol ; j++,l++) {				\
									\
	nhb_val = flgr2d_get_data_array_##dtype(pnhb, k, l*spp+n);	\
	if(nhb_val!=0) {						\
	  data_val = flgr2d_get_data_array_##dtype(pdat,i, j*spp+n);	\
	  list_coord_x[m] = l;						\
	  list_coord_y[m] = k;						\
	  list_data[m] = data_val;					\
	  list_nhb[m] = nhb_val;					\
	  m++;								\
	}								\
 									\
      }									\
    }									\
    size[n]=m;								\
  }									\
									\
  return




void flgr2d_get_neighborhood_fgBIT(FLGR_NhbBox2D *extr,
				   FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgBIT);
}

void flgr2d_get_neighborhood_fgUINT8(FLGR_NhbBox2D *extr,
				     FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgUINT8);
}

void flgr2d_get_neighborhood_fgUINT16(FLGR_NhbBox2D *extr,
				      FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgUINT16);
}

void flgr2d_get_neighborhood_fgUINT32(FLGR_NhbBox2D *extr,
				      FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgUINT32);
}

void flgr2d_get_neighborhood_fgINT8(FLGR_NhbBox2D *extr,
				    FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgINT8);
}

void flgr2d_get_neighborhood_fgINT16(FLGR_NhbBox2D *extr,
				     FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgINT16);
}

void flgr2d_get_neighborhood_fgINT32(FLGR_NhbBox2D *extr,
				     FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgINT32);
}

void flgr2d_get_neighborhood_fgFLOAT32(FLGR_NhbBox2D *extr,
				       FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgFLOAT32);
}

void flgr2d_get_neighborhood_fgFLOAT64(FLGR_NhbBox2D *extr,
				       FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA(fgFLOAT64);
}


//////////////////////////////////////////////////////////////////
/*! Extract a neighborhood in an image and copy to a neighborhood box
 *  Only relevant pixels are copied to neighborhood box.
 *  So the user must take car to extr->size to do computation
 *  @param extr : a pointer to a FLGR_NhbBox2D
 *  @param dat  : a pointer to a FLGR_Data2D considered as an image
 *  @param nhb  : a pointer to a FLGR_Data2D considered as a neighborhood definition
 *  @param row  : row index
 *  @param col  : col index
 *  @returns FLGR_NhbBox2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_get_neighborhood(FLGR_NhbBox2D *extr, 
				 FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  
  FLGR_DECLARE_FUNCTION;

  if((dat==NULL) || (nhb==NULL) || (extr==NULL)) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }


  if(dat->type != nhb->type) {
    POST_ERROR("Image structure and Neighborhood structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->type != nhb->type) {
    POST_ERROR("Neighbor Box and Neighborhood structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->nhb_size_x != nhb->size_x) {
    POST_ERROR("Neighbor Box and Neighborhood structure have differents size_x (%d and %d)!\n",extr->nhb_size_x,nhb->size_x);
    return FLGR_RET_SIZE_ERROR;
  }

  if(extr->nhb_size_y != nhb->size_y) {
    POST_ERROR("Neighbor Box and Neighborhood structure have differents size_y (%d and %d)!\n",extr->nhb_size_y,nhb->size_y);
    return FLGR_RET_SIZE_ERROR;
  }

  FLGR_DISPATCH_PROCEDURE(nhb->type,flgr2d_get_neighborhood,extr,dat,nhb,row,col);
  
  return FLGR_RET_OK;
}
  







//////////////////////////////////////////////////////////////////
// Macro for different type of getNeighborhood_dtype
//////////////////////////////////////////////////////////////////
#define FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(dtype)			\
  int i,ii,j,jj,m;							\
  int k,l,n;								\
  int nbrow,nbcol;							\
  int startrow,stoprow;							\
  int startcol,stopcol;							\
  int nhbsize_xdiv2;							\
  int nhbsize_ydiv2;							\
  int spp = dat->spp;							\
  dtype **pnhb = (dtype **) nhb->array;					\
  dtype **pdat = (dtype **) dat->array;					\
  dtype *list_data;							\
  dtype *list_nhb;							\
  int *list_coord_x;							\
  int *list_coord_y;							\
  int *size = extr->size;						\
  dtype nhb_val,data_val;						\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  nhbsize_xdiv2=(nhb->size_x)>>1;					\
  nhbsize_ydiv2=(nhb->size_y)>>1;					\
  nbrow=dat->size_y; nbcol=dat->size_x;					\
									\
  extr->center_coord_y=row;						\
  extr->center_coord_x=col;						\
									\
  startrow=row - nhbsize_ydiv2;						\
									\
  stoprow=row + nhbsize_ydiv2;						\
									\
  startcol=col - nhbsize_xdiv2;						\
									\
  stopcol=col + nhbsize_xdiv2;						\
									\
									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_data_val->array), \
				     (dtype*) (dat->array[row]),	\
				     spp, col);				\
									\
  flgr_get_data_array_vector_##dtype((dtype*) (extr->center_nhb_val->array), \
				     (dtype*) (nhb->array[nhbsize_ydiv2]), \
				     spp, nhbsize_xdiv2);		\
									\
									\
  for(n=0 ; n<spp ; n++) {						\
    list_data = (dtype*) extr->list_data_val[n];			\
    list_nhb = (dtype*) extr->list_nhb_val[n];				\
    list_coord_x = (int*) extr->list_coord_x[n];			\
    list_coord_y = (int*) extr->list_coord_y[n];			\
    m=0;								\
    for(i=startrow,k=0 ; i<=stoprow ; i++,k++) {			\
      ii = flgr_normalize_coordinate(i,dat->size_y);			\
      for(j=startcol,l=0 ; j<=stopcol ; j++,l++) {			\
									\
	nhb_val = flgr2d_get_data_array_##dtype(pnhb, k, l*spp+n);	\
	jj = flgr_normalize_coordinate(j,dat->size_x);			\
	data_val = flgr2d_get_data_array_##dtype(pdat,ii, jj*spp+n);	\
	list_coord_x[m] = l;						\
	list_coord_y[m] = k;						\
	list_data[m] = data_val;					\
	list_nhb[m] = nhb_val;						\
	m++;								\
 									\
      }									\
    }									\
    size[n]=m;								\
  }									\
									\
  return




void flgr2d_get_neighborhood_unfolded_fgBIT(FLGR_NhbBox2D *extr,
				   FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgBIT);
}

void flgr2d_get_neighborhood_unfolded_fgUINT8(FLGR_NhbBox2D *extr,
				     FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgUINT8);
}

void flgr2d_get_neighborhood_unfolded_fgUINT16(FLGR_NhbBox2D *extr,
				      FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgUINT16);
}

void flgr2d_get_neighborhood_unfolded_fgUINT32(FLGR_NhbBox2D *extr,
				      FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgUINT32);
}

void flgr2d_get_neighborhood_unfolded_fgINT8(FLGR_NhbBox2D *extr,
				    FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgINT8);
}

void flgr2d_get_neighborhood_unfolded_fgINT16(FLGR_NhbBox2D *extr,
				     FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgINT16);
}

void flgr2d_get_neighborhood_unfolded_fgINT32(FLGR_NhbBox2D *extr,
				     FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgINT32);
}

void flgr2d_get_neighborhood_unfolded_fgFLOAT32(FLGR_NhbBox2D *extr,
				       FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgFLOAT32);
}

void flgr2d_get_neighborhood_unfolded_fgFLOAT64(FLGR_NhbBox2D *extr,
				       FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  FLGR_MACRO_GET_NHB_2D_IN_DATA_UNFOLDED(fgFLOAT64);
}


//////////////////////////////////////////////////////////////////
/*! Extract a neighborhood_unfolded in an image and copy to a neighborhood_unfolded box
 *  Only relevant pixels are copied to neighborhood_unfolded box.
 *  So the user must take car to extr->size to do computation
 *  @param extr : a pointer to a FLGR_NhbBox2D
 *  @param dat  : a pointer to a FLGR_Data2D considered as an image
 *  @param nhb  : a pointer to a FLGR_Data2D considered as a neighborhood_unfolded definition
 *  @param row  : row index
 *  @param col  : col index
 *  @returns FLGR_NhbBox2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_get_neighborhood_unfolded(FLGR_NhbBox2D *extr, 
				 FLGR_Data2D *dat, FLGR_Data2D *nhb, int row, int col) {
  
  FLGR_DECLARE_FUNCTION;

  if((dat==NULL) || (nhb==NULL) || (extr==NULL)) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }


  if(dat->type != nhb->type) {
    POST_ERROR("Image structure and Neighborhood_Unfolded structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->type != nhb->type) {
    POST_ERROR("Neighbor Box and Neighborhood_Unfolded structure have differents types!\n");
    return FLGR_RET_TYPE_DIFFERENT;
  }

  if(extr->nhb_size_x != nhb->size_x) {
    POST_ERROR("Neighbor Box and Neighborhood_Unfolded structure have differents size_x (%d and %d)!\n",extr->nhb_size_x,nhb->size_x);
    return FLGR_RET_SIZE_ERROR;
  }

  if(extr->nhb_size_y != nhb->size_y) {
    POST_ERROR("Neighbor Box and Neighborhood_Unfolded structure have differents size_y (%d and %d)!\n",extr->nhb_size_y,nhb->size_y);
    return FLGR_RET_SIZE_ERROR;
  }

  FLGR_DISPATCH_PROCEDURE(nhb->type,flgr2d_get_neighborhood_unfolded,extr,dat,nhb,row,col);
  
  return FLGR_RET_OK;
}
  






//! @}


